package apibasej.basic.misc;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.logging.Level;

import apibasej.basic.config.ConfigManager;
import apibasej.basic.misc.io.UtilIO;

public class UtilHttp {

	public static final int TRIES_DEFAULT = 3;
	
	public static final String HEADER_CONTENT_LENGTH = "Content-Length";
	public static final String HEADER_CONTENT_TYPE = "Content-Type";
	
	
	public static byte[] getHttp(String url) throws IOException{
		return callHttp(url, null);
	}
	public static byte[] getHttp(String url, int connectTimeoutMillis, int readTimeoutMillis) throws IOException{
		return callHttp(url, false, connectTimeoutMillis, readTimeoutMillis, null, TRIES_DEFAULT);
	}
	public static byte[] getHttp(String url, int connectTimeoutMillis, int readTimeoutMillis, int tries) throws IOException{
		//System.out.println("connectTimeoutMillis="+connectTimeoutMillis+", readTimeoutMillis="+readTimeoutMillis);
		return callHttp(url, false, connectTimeoutMillis, readTimeoutMillis, null, tries);
	}
	
	public static byte[] callHttp(String url, byte[] toSend) throws IOException{
		return callHttp(url, toSend!=null, 12000, 33000, toSend, TRIES_DEFAULT);
	}
	
	public static byte[] callHttp(String url, boolean isPost, int connectTimeoutMillis, int readTimeoutMillis, byte[] toSend, int tries) throws IOException{
		
		URL url2 = new URL(url);
		HttpURLConnection conn = (HttpURLConnection)url2.openConnection(); // testei rodando local e rodando como applet. Quando roda como applet detecta as configurações do browser !!!! 
        
		try{
			if(toSend!=null && toSend.length>0){
				//precisa ??? tem como enviar dados com GET ???
				isPost = true;
			}
			conn.setRequestMethod(isPost?"POST":"GET");//"POST");
	        
			//conn.setRequestProperty("Accept", "*/*");
	        //conn.setRequestProperty("Cache-Control", "no-cache");
	        //conn.setRequestProperty("Pragma","no-cache");
	        //conn.setRequestProperty("Host",url2.getHost());/// precisa ???
	        //conn.setRequestProperty("User-Agent", getClass().getName());
	        //conn.setRequestProperty("Connection", "Keep-Alive"); //???????
	        
	        conn.setConnectTimeout(connectTimeoutMillis);
	        conn.setReadTimeout(readTimeoutMillis);
			conn.setUseCaches(false);
			conn.setDefaultUseCaches(false);
			
			//if(dataPost!=null && dataPost.length()>0) para não dar 405
			if(toSend!=null && toSend.length>0) {
				
				conn.setDoOutput(true);
				int contLenSend = toSend.length;
				
				conn.setRequestProperty(HEADER_CONTENT_LENGTH, ""+contLenSend );
				//http://developer.android.com/reference/java/net/HttpURLConnection.html
				conn.setFixedLengthStreamingMode(contLenSend);
				
				//DataOutputStream out = new DataOutputStream(conn.getOutputStream());
				//OutputStream out = new BufferedOutputStream(conn.getOutputStream());
				OutputStream out = conn.getOutputStream();
				out.write(toSend);
				try {
					out.flush();
					out.close();
				} catch (Throwable ioe) {
					ConfigManager.logDefAll(Level.SEVERE,ioe.getMessage(), ioe);
				}
			}else{
				conn.setDoOutput(false);
			}
			
			//------------ RESP ----------------
			//?não precisa tratar, pois se tentar ler a resposta com status diferente de 200 irá lançar exceção
			//statusResponseCode = conn.getResponseCode();
			
			byte[] respBytes = UtilIO.readInputStream(conn.getInputStream());
			
			//valida o Content-Length da resposta (ter flag para ignorar esta validação?)
			int contLenResp = conn.getContentLength();
			if(contLenResp>0 && contLenResp!=respBytes.length){
				
				String m = "Content-Length of the response does not match! Header Content-Length: "+contLenResp+", Bytes response length: "+respBytes.length;
				throw new IOException(m);
				
				/* ! O bug era na ServletFile que estava adicionando a str OK no final !!!
				//por algum motivo algumas leituras no linux está adicionando 2 caracteres a mais na leitura
				//neste caso remover os dois últimos caracteres e mostrar no log
				if(respBytes.length-2==contLenResp && StaticMisc.isLinux()){
					m += ", InfoResp: ";
					m += ("[...,"+respBytes[respBytes.length-2]+","+respBytes[respBytes.length-1]+"]");
					if(respBytes.length<30){
						m += ("["+new String(respBytes)+"]");
					}else{
						m += "[...]";
					}
					respBytes = Arrays.copyOf(respBytes, respBytes.length-2);
					ConfigManager.logDefAll(Level.SEVERE, m, new IOException(m));
				}else{
					throw new IOException(m);
				}*/
				
			}
			
			//???!!! ter opção de Content-SHA1 ???? (Content-SHA1 of the response does not match)
			//???>>>>>> colocar estas validações !!!
			//???>>> depois colocar um read timeout bem pequeno e testar as validações de execução de transações repetidas!
			
			return respBytes;
			
			/*
			//se quiser tratar e ler resposta com status diferente de 200
			try{//if(getStatusResponseCode()>=200 && getStatusResponseCode()<300) // REVER !!!!!!
				return conn.getInputStream();
			}catch(IOException e){//else 
				InputStream is = conn.getErrorStream();
				if(is!=null) return is;
				else throw e;
			}
			*/
			
		}catch(java.io.IOException ioEx){ //Todas estas são subclasses de IOException: java.net.SocketException | java.net.SocketTimeoutException | java.net.UnknownHostException | java.io.FileNotFoundException exToTry
			
			//tentar novamente, somente se a qtd de tentativas restantes for maior que 1
			if(tries>1){
				//registrar com level WARN ???
				ConfigManager.logDefAll(Level.WARNING, UtilHttp.class.getSimpleName()+": ERROR ON CALL HTTP! TRIES="+tries+", URL: "+url, ioEx);
				connectTimeoutMillis = (int)(connectTimeoutMillis*1.5); //aumenta em 50% o tempo para a próxima etntativa
				readTimeoutMillis = (int)(readTimeoutMillis*1.5);//aumenta em 50% o tempo para a próxima etntativa
				return callHttp(url, isPost, connectTimeoutMillis, readTimeoutMillis, toSend, --tries);//<<< !!! tem que diminuir a qtd de tentativas para não entrar em loop infinito
			}else{
				throw ioEx;
			}
			
		}finally{
			try {
				conn.disconnect();
			} catch (Throwable ioe) {
				ConfigManager.logDefAll(Level.SEVERE,ioe.getMessage(), ioe);
			}			
		}
		
	}
	
	/*
	
--------------------------------------------------------------------------------------------
- colocar opção de validação de Content-Length e Content-SHA1 se tiver estes headers no callHttp !!!
	-ter também opção de mais tentativas (aumentando o timeout?) mas somente para chamadas de leitura somente! transações não devem ter várias tentativas !!!
	 (conferir e analizar todas chamadas! mudar para UtilHttp.callHttp...)
* Exemplos de erros:
-----------------------
Caused by: java.net.SocketException: Unexpected end of file from server
	at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
	at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
	at apibasej.basic.StaticMisc.callHttp(StaticMisc.java:175)
-----------------------
Caused by: java.net.SocketTimeoutException: connect timed out
	at java.net.TwoStacksPlainSocketImpl.socketConnect(Native Method)
	at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
	at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
	at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
	at java.net.PlainSocketImpl.connect(Unknown Source)
	at java.net.SocksSocketImpl.connect(Unknown Source)
	at java.net.Socket.connect(Unknown Source)
	at sun.net.NetworkClient.doConnect(Unknown Source)
	at sun.net.www.http.HttpClient.openServer(Unknown Source)
	at sun.net.www.http.HttpClient.openServer(Unknown Source)
	at sun.net.www.http.HttpClient.<init>(Unknown Source)
	at sun.net.www.http.HttpClient.New(Unknown Source)
	at sun.net.www.http.HttpClient.New(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
	at apibasej.basic.StaticMisc.callHttp(StaticMisc.java:156)
----------------------
java.net.UnknownHostException: meXrcsis.no-ip.org
	at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
	at java.net.PlainSocketImpl.connect(Unknown Source)
	at java.net.SocksSocketImpl.connect(Unknown Source)
	at java.net.Socket.connect(Unknown Source)
	at sun.net.NetworkClient.doConnect(Unknown Source)
	at sun.net.www.http.HttpClient.openServer(Unknown Source)
	at sun.net.www.http.HttpClient.openServer(Unknown Source)
	at sun.net.www.http.HttpClient.<init>(Unknown Source)
	at sun.net.www.http.HttpClient.New(Unknown Source)
	at sun.net.www.http.HttpClient.New(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
	at apibasej.basic.StaticMisc.callHttp(StaticMisc.java:156)	
-----------------
java.net.NoRouteToHostException: No route to host: connect
	at java.net.TwoStacksPlainSocketImpl.socketConnect(Native Method)
	at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
	at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
	at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
	at java.net.PlainSocketImpl.connect(Unknown Source)
	at java.net.SocksSocketImpl.connect(Unknown Source)
	at java.net.Socket.connect(Unknown Source)
	at sun.net.NetworkClient.doConnect(Unknown Source)
	at sun.net.www.http.HttpClient.openServer(Unknown Source)
	at sun.net.www.http.HttpClient.openServer(Unknown Source)
	at sun.net.www.http.HttpClient.<init>(Unknown Source)
	at sun.net.www.http.HttpClient.New(Unknown Source)
	at sun.net.www.http.HttpClient.New(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
	at apibasej.basic.misc.UtilHttp.callHttp(UtilHttp.java:67)
-----------------
Caused by: java.io.FileNotFoundException: http://189.74.9.187/m-3-10-15/ServletRes?o=k
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
        at apibasej.basic.StaticMisc.callHttp(StaticMisc.java:175)
        at apibasej.basic.StaticMisc.callHttp(StaticMisc.java:117)
        at juliolessa.resourcemanager.client.remote.ClientLauncherClassLoaderRemote.getChSimServ(ClientLauncherClassLoaderRemote.java:22)
        at juliolessa.resourcemanager.client.classes.AbstractLoader.initKey(AbstractLoader.java:123)
----------------------
(!!!) este erro não deve ser tratado como erro de conexão
Caused by: java.io.IOException: Server returned HTTP response code: 500 for URL: http://mXercsis.no-ip.org/rest/GetRes?v=3-5-20
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
	at apibasej.basic.StaticMisc.callHttp(StaticMisc.java:175)
--------------------------------------------------------------------------------------------	
	
	*/

}
